package com.fivium.bpd.io;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.fivium.bpd.BPDEdit;
import com.fivium.bpd.component.Assignment;
import com.fivium.bpd.component.Assignment.ASSIGNMENT_TYPE;
import com.fivium.bpd.component.BusinessProcessDefinition;
import com.fivium.bpd.component.EntryStage;
import com.fivium.bpd.component.ExitStage;
import com.fivium.bpd.component.QueryDefinition;
import com.fivium.bpd.component.StandardStage;

public class BPDLoader
{
  public static final String XPATH_BPD_SHORT_NAME   = "/BUSINESS_PROCESS_DEFINITION/SHORT_NAME";
  public static final String XPATH_BPD_FULL_NAME    = "/BUSINESS_PROCESS_DEFINITION/FULL_NAME";
  public static final String XPATH_BPD_DESCRIPTION  = "/BUSINESS_PROCESS_DEFINITION/DESCRIPTION";
  
  public static final String XPATH_ASSIGNMENT_PROCESS           = "/BUSINESS_PROCESS_DEFINITION/ASSIGNMENTS/PROCESS_ASSIGNMENT_LIST/PROCESS_ASSIGNMENT";
  public static final String XPATH_ASSIGNMENT_CONTEXTUAL        = "/BUSINESS_PROCESS_DEFINITION/ASSIGNMENTS/CONTEXTUAL_ASSIGNMENT_LIST/CONTEXTUAL_ASSIGNMENT";
  public static final String XPATH_ASSIGNMENT_EXTERNAL          = "/BUSINESS_PROCESS_DEFINITION/ASSIGNMENTS/EXTERNAL_ASSIGNMENT_LIST/EXTERNAL_ASSIGNMENT";
  public static final String XPATH_ASSIGNMENT_ASSIGNMENT        = "ASSIGNMENT".intern();
  public static final String XPATH_ASSIGNMENT_ASSIGNMENT_TITLE  = "ASSIGNMENT_TITLE".intern();
  public static final String XPATH_ASSIGNMENT_DELEGATION        = "DELEGATION".intern();
  
  public static final String XPATH_QUERY_DEFINITION = "/BUSINESS_PROCESS_DEFINITION/QUERY_DEFINITION_LIST/QUERY_DEFINITION";
  public static final String XPATH_QUERY_DEFINITION_QUERY_NAME = "QUERY_NAME";
  public static final String XPATH_QUERY_DEFINITION_SQL = "SQL";
  
  public static final String XPATH_STANDARD_STAGE = "/BUSINESS_PROCESS_DEFINITION/PROCESS_STATES/STANDARD_STAGE_LIST/STAGE";
  public static final String XPATH_STANDARD_STAGE_LABEL = "STAGE_LABEL";
  public static final String XPATH_STANDARD_STAGE_TITLE = "STAGE_TITLE";
  
  public static final String XPATH_ENTRY_STAGE = "/BUSINESS_PROCESS_DEFINITION/PROCESS_STATES/ENTRY_STAGE_LIST/STAGE";
  public static final String XPATH_ENTRY_STAGE_LABEL = "STAGE_LABEL";
  public static final String XPATH_ENTRY_STAGE_TITLE = "STAGE_TITLE";
  
  public static final String XPATH_EXIT_STAGE = "/BUSINESS_PROCESS_DEFINITION/PROCESS_STATES/EXIT_STAGE_LIST/STAGE";
  public static final String XPATH_EXIT_STAGE_LABEL = "STAGE_LABEL";
  public static final String XPATH_EXIT_STAGE_TITLE = "STAGE_TITLE";
  
  public static void loadBusinessProcessDefinition(String path)
  {
    try {
      DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
      DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
      Document doc = docBuilder.parse (new File(path));
      doc.getDocumentElement().normalize();
      
      BusinessProcessDefinition bpd = new BusinessProcessDefinition();
      
      loadMetaData(bpd, doc);
      loadAssignments(bpd, doc, ASSIGNMENT_TYPE.PROCESS);
      loadAssignments(bpd, doc, ASSIGNMENT_TYPE.CONTEXTUAL);
      loadAssignments(bpd, doc, ASSIGNMENT_TYPE.EXTERNAL);
      loadStandardStages(bpd, doc);
      loadEntryStages(bpd, doc);
      loadExitStages(bpd, doc);
      loadQueries(bpd, doc);
      
      
      BPDEdit.openBusinessProcessDefinition(bpd);      
    }
    catch (ParserConfigurationException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    catch (SAXException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  
  private static void loadMetaData(BusinessProcessDefinition bpd, Document doc)
  {
    XPathFactory xpFactory = XPathFactory.newInstance();
    XPath xpath = xpFactory.newXPath();
    XPathExpression xpExpr;
    Node node;
    
    try {
      xpExpr = xpath.compile(XPATH_BPD_SHORT_NAME);
      node = (Node)xpExpr.evaluate(doc, XPathConstants.NODE);
      bpd.setShortName(node.getTextContent());
    }
    catch (XPathExpressionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    
    try {
      xpExpr = xpath.compile(XPATH_BPD_FULL_NAME);
      node = (Node)xpExpr.evaluate(doc, XPathConstants.NODE);
      bpd.setFullName(node.getTextContent());
    }
    catch (XPathExpressionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    
    try {
      xpExpr = xpath.compile(XPATH_BPD_DESCRIPTION);
      node = (Node)xpExpr.evaluate(doc, XPathConstants.NODE);
      bpd.setDescription(node.getTextContent());
    }
    catch (XPathExpressionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  
  private static void loadAssignments(BusinessProcessDefinition bpd, Document doc, ASSIGNMENT_TYPE assignmentType)
  {
    XPathFactory xpFactory = XPathFactory.newInstance();
    XPath xpath = xpFactory.newXPath();
    XPathExpression xpExpr;
    NodeList nodes;
    
    try {
      xpExpr = null;
      switch (assignmentType) {
        case PROCESS:
          xpExpr = xpath.compile(XPATH_ASSIGNMENT_PROCESS);
          break;
        case CONTEXTUAL:
          xpExpr = xpath.compile(XPATH_ASSIGNMENT_CONTEXTUAL);
          break;
        case EXTERNAL:
          xpExpr = xpath.compile(XPATH_ASSIGNMENT_EXTERNAL);
          break;
      }
      
      nodes = (NodeList)xpExpr.evaluate(doc, XPathConstants.NODESET);
      
      for (int i = 0; i < nodes.getLength(); i++) {
        Assignment assignment = new Assignment();
        bpd.addAssignment(assignment);
        
        assignment.setAssignmentType(assignmentType); 
        
        NodeList childNodes = nodes.item(i).getChildNodes();
        
        for (int j = 0; j < childNodes.getLength(); j++) {
          Node node = childNodes.item(j);
          
          String nodeName = node.getNodeName().intern(); 
          if (nodeName == XPATH_ASSIGNMENT_ASSIGNMENT) {
            assignment.setAssignment(node.getTextContent());
          } else if (nodeName == XPATH_ASSIGNMENT_ASSIGNMENT_TITLE) {
            assignment.setAssignmentTitle(node.getTextContent());
          } else if (nodeName == XPATH_ASSIGNMENT_DELEGATION) {
            assignment.setDelegation(node.getTextContent());
          }
        }
      }
      
    }
    catch (XPathExpressionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  
  private static void loadQueries(BusinessProcessDefinition bpd, Document doc)
  {
    XPathFactory xpFactory = XPathFactory.newInstance();
    XPath xpath = xpFactory.newXPath();
    XPathExpression xpExpr;
    NodeList nodes;
    
    try {
      xpExpr = xpath.compile(XPATH_QUERY_DEFINITION);
      nodes = (NodeList)xpExpr.evaluate(doc, XPathConstants.NODESET);
      
      for (int i = 0; i < nodes.getLength(); i++) {
        QueryDefinition queryDefinition = new QueryDefinition();
        bpd.addQueryDefinition(queryDefinition);
        
        NodeList childNodes = nodes.item(i).getChildNodes();
        
        for (int j = 0; j < childNodes.getLength(); j++) {
          Node node = childNodes.item(j);
          
          String nodeName = node.getNodeName().intern(); 
          if (nodeName == XPATH_QUERY_DEFINITION_QUERY_NAME) {
            queryDefinition.setName(node.getTextContent());
          } else if (nodeName == XPATH_QUERY_DEFINITION_SQL) {
            queryDefinition.setSQL(node.getTextContent());
          }
        }
      }
      
    }
    catch (XPathExpressionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  
  private static void loadStandardStages(BusinessProcessDefinition bpd, Document doc)
  {
    XPathFactory xpFactory = XPathFactory.newInstance();
    XPath xpath = xpFactory.newXPath();
    XPathExpression xpExpr;
    NodeList nodes;
    
    try {
      xpExpr = xpath.compile(XPATH_STANDARD_STAGE);
      nodes = (NodeList)xpExpr.evaluate(doc, XPathConstants.NODESET);
      
      for (int i = 0; i < nodes.getLength(); i++) {
        StandardStage standardStage = new StandardStage(120d, 80d, 10d, 10d);
        bpd.addStandardStage(standardStage);
        
        NodeList childNodes = nodes.item(i).getChildNodes();
        
        for (int j = 0; j < childNodes.getLength(); j++) {
          Node node = childNodes.item(j);
          
          String nodeName = node.getNodeName().intern(); 
          if (nodeName == XPATH_STANDARD_STAGE_LABEL) {
            standardStage.setLabel(node.getTextContent());
          } else if (nodeName == XPATH_STANDARD_STAGE_TITLE) {
            standardStage.setTitle(node.getTextContent());
          }
        }
      }
      
    }
    catch (XPathExpressionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  
  private static void loadEntryStages(BusinessProcessDefinition bpd, Document doc)
  {
    XPathFactory xpFactory = XPathFactory.newInstance();
    XPath xpath = xpFactory.newXPath();
    XPathExpression xpExpr;
    NodeList nodes;
    
    try {
      xpExpr = xpath.compile(XPATH_ENTRY_STAGE);
      nodes = (NodeList)xpExpr.evaluate(doc, XPathConstants.NODESET);
      
      for (int i = 0; i < nodes.getLength(); i++) {
        EntryStage entryStage = new EntryStage(120d, 40d, 10d, 10d);
        bpd.addEntryStage(entryStage);
        
        NodeList childNodes = nodes.item(i).getChildNodes();
        
        for (int j = 0; j < childNodes.getLength(); j++) {
          Node node = childNodes.item(j);
          
          String nodeName = node.getNodeName().intern(); 
          if (nodeName == XPATH_ENTRY_STAGE_LABEL) {
            entryStage.setLabel(node.getTextContent());
          } else if (nodeName == XPATH_ENTRY_STAGE_TITLE) {
            entryStage.setTitle(node.getTextContent());
          }
        }
      }
      
    }
    catch (XPathExpressionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  
  private static void loadExitStages(BusinessProcessDefinition bpd, Document doc)
  {
    XPathFactory xpFactory = XPathFactory.newInstance();
    XPath xpath = xpFactory.newXPath();
    XPathExpression xpExpr;
    NodeList nodes;
    
    try {
      xpExpr = xpath.compile(XPATH_EXIT_STAGE);
      nodes = (NodeList)xpExpr.evaluate(doc, XPathConstants.NODESET);
      
      for (int i = 0; i < nodes.getLength(); i++) {
        ExitStage exitStage = new ExitStage(120d, 40d, 10d, 10d);
        bpd.addExitStage(exitStage);
        
        NodeList childNodes = nodes.item(i).getChildNodes();
        
        for (int j = 0; j < childNodes.getLength(); j++) {
          Node node = childNodes.item(j);
          
          String nodeName = node.getNodeName().intern(); 
          if (nodeName == XPATH_EXIT_STAGE_LABEL) {
            exitStage.setLabel(node.getTextContent());
          } else if (nodeName == XPATH_EXIT_STAGE_TITLE) {
            exitStage.setTitle(node.getTextContent());
          }
        }
      }
      
    }
    catch (XPathExpressionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}
